#ifndef COMMON_UINT_SEQ_H
#define COMMON_UINT_SEQ_H

#include <cstdint>
#include <memory>
#include <common/exception.h>
#include <cstring>

class IUintSeq
{
public:
    virtual ~IUintSeq() = default;;
    virtual void Reserve(size_t size) = 0;
    [[nodiscard]] virtual size_t GetIndex(size_t index) const = 0;
    virtual void SetIndex(size_t index, size_t num) = 0;
    [[nodiscard]] virtual size_t GetLength() const = 0;
    [[nodiscard]] virtual const uint8_t* Data() const = 0;
    [[nodiscard]] virtual size_t GetDataLength() const = 0;
};

class Uint8Seq : public IUintSeq
{
private:
    uint8_t* data = nullptr;
    size_t length;
public:
    explicit Uint8Seq(size_t size)
    {
        data = new uint8_t[size];
        length = size;
    }
    Uint8Seq(uint8_t* data, size_t size)
    {
        data = new uint8_t[size];
        length = size;
        memcpy(this->data, data, size);
    }

    ~Uint8Seq() override
    {
        delete data;
    }
    void Reserve(size_t size) override
    {
        if (size == length)
            return;
        auto newData = new uint8_t[size];
        if (size > length)
            memcpy(newData, data, length);
        else
            memcpy(newData, data, size);
        length = size;
        data = newData;
    }
    [[nodiscard]] size_t GetIndex(size_t index) const override
    {
        if (index >= length)
            throw ArrayIndexOutOfBoundsException("Uint8Seq GetIndex index out of bounds.");
        return *(data + index);
    }
    void SetIndex(size_t index, size_t num) override
    {
        if (index >= length)
            throw ArrayIndexOutOfBoundsException("Uint8Seq SetIndex index out of bounds.");
        if (num > 255)
            throw OutOfRangeException("Uint8Seq SetIndex num out of range(0~255).");
        *(data + index) = num;
    }
    [[nodiscard]] size_t GetLength() const override
    {
        return length;
    }
    [[nodiscard]] const uint8_t* Data() const override
    {
        return data;
    };
    [[nodiscard]] size_t GetDataLength() const override
    {
        return length;
    };
    uint8_t* ptr() { return data; }
    void swap(Uint8Seq& o)
    {
        uint8_t* t;
        size_t l;
        t = o.data;
        o.data = data;
        data = t;
        l = o.length;
        o.length = length;
        length = l;
    }
};

class Uint16Seq : public IUintSeq
{
private:
    uint16_t* data = nullptr;
    size_t length;
public:
    explicit Uint16Seq(size_t size)
    {
        data = new uint16_t[size];
        length = size;
    }
    Uint16Seq(uint16_t* data, size_t size)
    {
        data = new uint16_t[size];
        length = size;
        memcpy(this->data, data, size * 2);
    }
    ~Uint16Seq() override
    {
        delete data;
    }
    void Reserve(size_t size) override
    {
        if (size == length)
            return;
        auto newData = new uint16_t[size];
        if (size > length)
            memcpy(newData, data, length * 2);
        else
            memcpy(newData, data, size * 2);
        length = size;
        data = newData;
    }
    [[nodiscard]] size_t GetIndex(size_t index) const override
    {
        if (index >= length)
            throw ArrayIndexOutOfBoundsException("Uint16Seq GetIndex index out of bounds.");
        return *(data + index);
    }
    void SetIndex(size_t index, size_t num) override
    {
        if (index >= length)
            throw ArrayIndexOutOfBoundsException("Uint16Seq SetIndex index out of bounds.");
        if (num > 65535)
            throw OutOfRangeException("Uint16Seq SetIndex num out of range(0~65535).");
        *(data + index) = num;
    }
    [[nodiscard]] size_t GetLength() const override
    {
        return length;
    }
    [[nodiscard]] const uint8_t* Data() const override
    {
        return (uint8_t*)data;
    };
    [[nodiscard]] size_t GetDataLength() const override
    {
        return length * 2;
    };
    void swap(Uint16Seq& o)
    {
        uint16_t* t;
        size_t l;
        t = o.data;
        o.data = data;
        data = t;
        l = o.length;
        o.length = length;
        length = l;
    }
};

#endif //COMMON_UINT_SEQ_H
